package jp.ac.waseda.cs.washi.game.ai;

//PlayerAction.LEFT を LEFT と記述できるようにする宣言
import static jp.ac.waseda.cs.washi.game.api.PlayerAction.*;

import java.util.ArrayList;
import java.util.Arrays;

import jp.ac.waseda.cs.washi.game.api.CharaInfo;
import jp.ac.waseda.cs.washi.game.api.GameInfo;
import jp.ac.waseda.cs.washi.game.api.MapInfo;
import jp.ac.waseda.cs.washi.game.api.Player;
import jp.ac.waseda.cs.washi.game.api.PlayerAction;
import jp.ac.waseda.cs.washi.gameaiarena.api.Direction4;
import jp.ac.waseda.cs.washi.gameaiarena.api.Point2;

public class Meteor2KingPumpkin implements Player {

	MapInfo map = null;
	CharaInfo mychara = null;
	CharaInfo enemy = null;

	Point2 dstPoint = null;
	boolean reachPoint = false;
	boolean finishCircle = false;
	boolean finishPumpkin = false;

	Direction4[] root = {};
	ArrayList<PlayerAction> actionList;
	private final PlayerAction[] CircleAction = { LEFT, LEFT, DOWN, DOWN, DOWN,
			DOWN, RIGHT, RIGHT, RIGHT, RIGHT, UP, UP, UP, UP, LEFT, LEFT, NONE };
	private final PlayerAction[] KingPumpkinAction = { LEFT, CLEAR, LEFT,
			CLEAR, DOWN, CLEAR, DOWN, DOWN, CLEAR, DOWN, CLEAR, RIGHT, CLEAR,
			RIGHT, RIGHT, CLEAR, RIGHT, CLEAR, UP, CLEAR, UP, UP, CLEAR, UP,
			CLEAR, LEFT, CLEAR, LEFT };

	// マップ情報、自プレイヤー情報、敵プレイヤー情報の取得
	@Override
	public void initialize(GameInfo env) {
		this.map = env.getMap();
		this.mychara = env.getPlayer();
		this.enemy = env.getEnemy();

		dstPoint = null;
		reachPoint = false;
		finishCircle = false;
		finishPumpkin = false;

		actionList = new ArrayList<PlayerAction>();
	}

	@Override
	public PlayerAction execute(GameInfo env) {
		// 目的地を設定（あとで囲い込みするために、端にはいかないようにする）
		Direction4[] direction;
		if (dstPoint == null) {
			int count = 0;
			do {
				dstPoint = new Point2(
						(int) (Math.random() * map.getWidth() - 5),
						(int) (Math.random() * map.getHeight() - 5));
				direction = map.getPath(mychara.getLocation(), dstPoint);

				// 周囲に自分のボーナス図形がないことを確認する
				if (findMyBonus(dstPoint.add(new Point2(0, 2)))) {
					direction = null;
				}
			} while (direction == null && (count++ < 20));
			if (count > 20)
				return NONE;
			for (Direction4 dir : direction) {
				actionList.add(PlayerAction.fromDirection(dir));
			}
		}

		// 目的地へ移動する
		if (!reachPoint) {
			// 目的地に着いたら、囲い込みに移行
			if (dstPoint.equals(mychara.getLocation())) {
				// もし、目的地に着いたときにactionListに値が残っていたら全て捨てる
				while (actionList.size() != 0)
					actionList.remove(0);
				reachPoint = true;

				// 囲い込み用アクションリストを追加
				actionList.addAll(Arrays.asList(CircleAction));
			}

			// 目的地へ移動
			if (actionList.size() != 0) {
				return actionList.remove(0);
			}
		}

		// ５×５の囲い込みを行う
		if (reachPoint && !finishCircle) {
			// 囲い込みアクションが全て終了したら、整形に進む
			// アクション終了時に、囲い込みが完了していなければ、別の場所に移動する
			if (actionList.size() == 0) {
				if (cirleComplete()) {
					while (actionList.size() != 0)
						actionList.remove(0);
					finishCircle = true;

					// 整形用アクションリストを追加
					actionList.addAll(Arrays.asList(KingPumpkinAction));
				} else {
					dstPoint = null;
					reachPoint = false;
					finishCircle = false;
					return NONE;
				}
			}

			// 足元がdevelopされていなければdevelopする
			if (map.getRequiredDevelopCount(mychara, mychara.getLocation()) != 0) {
				return SPELL;
			} else {
				return actionList.remove(0);
			}
		}

		// 終了したら、KingPumpkinとなるように整形１（囲い込み後の、KingPumpkinに邪魔な部分をClearする）
		if (finishCircle && !finishPumpkin) {
			// 整形１で必要なアクション終了時
			if (actionList.size() == 0) {
				actionList.addAll(getRemovePoint());
				finishPumpkin = true;
			} else {
				return actionList.remove(0);
			}
		}

		// 終了したら、KingPumpkinとなるように整形２（囲い込みの外側の、KingPumpkinに邪魔な部分をClearする）
		if (finishPumpkin) {
			// 全てが終了したら初期化
			if (actionList.size() == 0) {
				initialize(env);
			} else {
				return actionList.remove(0);
			}
		}

		return NONE;
	}

	// dstPointの周辺に自分のボーナス図形が存在しないことを確認する
	private boolean findMyBonus(Point2 dstPoint) {
		int baseX = dstPoint.x;
		int baseY = dstPoint.y;

		for (int i = -3; i <= 3; i++) {
			for (int j = -3; j <= 3; j++) {
				if (!map.isAvailable(baseX + i, baseY + j)) {
					continue;
				}
				// 自分か相手がもっているボーナス図形に含まれているとき
				if (map.getRequiredDevelopCount(mychara, baseX + i, baseY + j) == 0
						&& map.getRequiredDevelopCount(enemy, baseX + i, baseY
								+ j) == 0) {
					return true;
				}
			}
		}

		return false;
	}

	private ArrayList<PlayerAction> getRemovePoint() {
		ArrayList<PlayerAction> resultList = new ArrayList<PlayerAction>();

		int baseX = mychara.getLocation().x;
		int baseY = mychara.getLocation().y;
		Point2[] checkPoints = { new Point2(baseX, baseY - 1),
				new Point2(baseX - 3, baseY + 2),
				new Point2(baseX + 3, baseY + 2),
				new Point2(baseX + 0, baseY + 5), };

		Point2 prevPt = mychara.getLocation();
		for (Point2 checkPt : checkPoints) {
			if (map.getRequiredDevelopCount(mychara, checkPt) == 0
					&& !map.isWall(checkPt)) {
				Direction4[] dirs = map.getPath(prevPt, checkPt);
				prevPt = checkPt;

				if (dirs != null) {
					for (Direction4 dir : dirs) {
						resultList.add(PlayerAction.fromDirection(dir));
					}
				}
				resultList.add(CLEAR);
			}
		}

		return resultList;
	}

	// 囲い込み終了時、囲い込みが正常に行われたかどうかを返す
	// （障害物や敵プレイヤーによる妨害によって囲い込みが正常に完了していない場合falseとなる）
	private boolean cirleComplete() {
		int baseX = mychara.getLocation().x;
		int baseY = mychara.getLocation().y;
		Point2[] checkPoints = { new Point2(baseX - 2, baseY + 0),
				new Point2(baseX - 1, baseY + 0),
				new Point2(baseX + 0, baseY + 0),
				new Point2(baseX + 1, baseY + 0),
				new Point2(baseX + 2, baseY + 0),
				new Point2(baseX - 2, baseY + 1),
				new Point2(baseX - 1, baseY + 1),
				new Point2(baseX + 0, baseY + 1),
				new Point2(baseX + 1, baseY + 1),
				new Point2(baseX + 2, baseY + 1),
				new Point2(baseX - 2, baseY + 2),
				new Point2(baseX - 1, baseY + 2),
				new Point2(baseX + 0, baseY + 2),
				new Point2(baseX + 1, baseY + 2),
				new Point2(baseX + 2, baseY + 2),
				new Point2(baseX - 2, baseY + 3),
				new Point2(baseX - 1, baseY + 3),
				new Point2(baseX + 0, baseY + 3),
				new Point2(baseX + 1, baseY + 3),
				new Point2(baseX + 2, baseY + 3),
				new Point2(baseX - 2, baseY + 4),
				new Point2(baseX - 1, baseY + 4),
				new Point2(baseX + 0, baseY + 4),
				new Point2(baseX + 1, baseY + 4),
				new Point2(baseX + 2, baseY + 4), };

		for (Point2 checkPt : checkPoints) {
			if (map.getRequiredDevelopCount(mychara, checkPt) != 0) {
				return false;
			}
		}
		return true;
	}
}
